Spring Security Logout
✒️ 2025-07-02 11:27 내용 수정
- 참고 자료 : Spring Security Handling Logouts 공식 문서, 스프링 시큐리티 기본 API 및 Filter 이해 - Logout처리, LogoutFilter
- 정리 내용은 공식 문서의 내용을 참고하여 작성하였다.
- Spring Security는 기본으로
/logout엔드포인트를 사용하며, 이를 그대로 사용하면 추가적인 코드를 사용할 필요가 없다. LogoutFilter는 filter chain의AuthorizationFilter보다 전에 등장하므로, 기본/logout엔드포인트를 따로 허용할 필요가 없다.- 로그인 설정과 마찬가지로 Controller에서
POST /logout을 새로 지정하지 않아도SecurityFilter중LogoutFilter가 요청을 가로채 로그아웃을 처리한다. - Spring Security Architecture 참고.
- 로그인 설정과 마찬가지로 Controller에서
- 다만 커스텀 로그아웃 엔드포인트를 사용하거나 logout 성공 url 등의 설정이 필요하다면
SecurityFilterChain에서permitAll설정을 적용하여 endpoint에 도달할 수 있도록 한다.
Logout 구조
spring-boot-starter-security의존성을 추가하거나@EnableWebSecurityAnnotation을 사용한다면, Spring Security는 기본으로GET /logout과POST /logout에 응답과 logout 지원을 추가한다.
GET /logout
GET /logout을 요청하면 Spring Security는 로그아웃 확정 페이지를 보여준다.- 로그아웃 재확인을 사용자에게 보여줄 수 있다.
POST /logout에 필요한 CSRF Token도 페이지에 포함된다.
- 만약 설정에서 CSRF 보호가 꺼진 상태라면 logout 확정 페이지가 뜨지 않으며, logout이 바로 실행된다.
GET /logout이 설정되지 않더라도 요청에 CSRF Token이 포함되어 있으면POST /logout으로 로그아웃할 수 있다.
POST /logout
POST /logout요청을 전송한다.LogoutFilter가 요청을 가로채어LogoutHandler를 호출한다.SecurityContextLogoutHandler가 로그아웃과 연관된 객체 등을 정리한다.- HTTP Session을 만료 시킨다. 쿠키도 설정에 따라 함께 제거할 수 있다.
SecurityContextHolderStrategy를 비운다.SecurityContextRepository를 비운다.
TokenRememberMeServices나PersistentTokenRememberMeServices에서 RememberMe authentication을 비운다.CsrfLogoutHandler에서 저장된 CSRF Token을 제거한다.LogoutSuccessEventPublishingLogoutHandler에서LogoutSuccessEvent를 내보낸다.- 기본
LogoutSuccessHandler가/login?logout으로 리다이렉트 시킨다.
커스텀 Logout URI 설정
- 공식 문서 및 예시는 Spring Security 6.x 버전 이상 기준이다.
- 커스텀 Logout URI를 설정하려면
FilterChain에서.logout()의.logoutUrl을 추가한다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.logout((logout) -> logout.logoutUrl("/my/logout/uri"));
// ...
}
- 만약 커스텀 logout 성공 endpoint를 따로 지정한다면(Spring MVC를 사용한다 했을 때) Spring Security에서 이를 허용하도록 설정해야 한다.
- Spring MVC는 Spring Security가 실행된 이후에 요청을 처리하기 때문이다.
- 공식 문서 예시에선 logout을 수행한 뒤
/my/success/endpoint로 리다이렉트 하도록LogoutFilter에 적용하고,AuthorizationFilter에는/my/success/endpointendpoint를 허용하도록 설정하는 예시다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests((authorize) -> authorize
// 커스텀 endpoint를 허용하도록 설정
.requestMatchers("/my/success/endpoint").permitAll()
// ...
)
// 커스텀 logout 성공 endpoint 설정
.logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint"));
// ...
}
// Java configuration 사용 시
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// 커스텀 logout 성공 endpoint 설정하고 허용
.logout((logout) -> logout
.logoutSuccessUrl("/my/success/endpoint")
.permitAll()
);
// ...
}
- 보통 logout을 위한 Spring MVC endpoint를 생성하는 것 보다 커스텀
LogoutHandler를 등록하는 것이 더 간단하다.- 종종 적절한 logout 동작을 위해 Spring Security component를 호출하는 것을 잊기 쉽기 때문에 공식 문서에선 제공된
logoutDSL를 사용하여 logout을 설정하는 것을 추천한다.
- 종종 적절한 logout 동작을 위해 Spring Security component를 호출하는 것을 잊기 쉽기 때문에 공식 문서에선 제공된
- 하지만 커스텀 endpoint를 사용해야 한다면
SecurityContextLogoutHandler를 사용하여 안전하고 완벽한 logout을 수행할 수 있도록 할 수 있다.- 공식 문서의 예시는 최소한의 세팅이라 설명되어 있고, 이 동작은 필요 시
SecurityContextHoderStrategy와SecurityCOntextRepository를 비운다고 한다.
- 공식 문서의 예시는 최소한의 세팅이라 설명되어 있고, 이 동작은 필요 시
public class DemoController {
// LogoutHandler 생성
SecurityContextLogoutHandler logoutHandler
= new SecurityContextLogoutHandler();
@PostMapping("/my/logout")
public String performLogout(
Authentication authentication,
HttpServletRequest request,
HttpServletResponse response
) {
// .. logout 수행
this.logoutHandler
.doLogout(
request,
response,
authentication
);
return "redirect:/home";
}
}
Clean-up action 추가
- Java configuration 사용 시
addLogoutHandler()를 사용하여logout에LogoutHandler를 추가할 수 있다. LogoutHandler는 clean-up 목적으로 사용되기에 예외를 던지면 안된다.LogoutHandler는 인터페이스기 때문에 lambda 표현식을 사용하여 직접 커스텀LogoutHandler를 구현해 사용할 수 있다.- 람다식 참고.
// Java configuration 사용 시
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
// 쿠키 제거 LogoutHandler
CookieClearingLogoutHandler cookies = new CookieClearingLogoutHandler("custom-cookie");
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// 쿠키를 비우는 LogoutHandler 추가
.logout((logout) -> logout
.addLogoutHandler(cookies)
);
// ...
}
- 몇몇
LogoutHandler설정은 공통적이기에 직접logoutDSL과<logout>element에 노출되며, 적절한 설정 값만 넘겨주면 바로 사용할 수 있다.JSESSIONIDcookie는SecurityContextLogoutHandler가 만료된 session을 제거하기 때문에 따로 삭제 코드를 작성할 필요가 없다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// 특정 쿠키 제거
.logout((logout) -> logout
.deleteCookies("custom-cookie")
);
// ...
}
clearAuthentication()와invalidateHttpSession()는 기본으로true이기에 logout 시 자동으로 session이 만료되고Authentication객체가 제거된다.- 필요에 따라 session이나
Authentication객체를 logout 후에도 유지한다면false로 설정을 추가한다.
- 필요에 따라 session이나
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// 특정 쿠키 제거
.logout((logout) -> logout
.invalidateHttpSession(false)
.clearAuthentication(false)
);
// ...
}
Clear-Site-DataHTTP 헤더는 웹 사이트가 소유하는 cookie, storage, cache를 삭제하도록 브라우저가 지원하는 헤더다.- 이는 sessin cookie를 포함해 logout시 모든 것을 삭제하는 안전하고 간단한 방법이다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
// ClearSiteDataHeader를 작성하는 LogoutHandler 생성
HeaderWriterLogoutHandler clearSiteData =
new HeaderWriterLogoutHandler(new ClearSiteDataHeaderWriter());
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// LogoutHanler 추가
.logout((logout) -> logout
.addLogoutHandler(clearSiteData)
);
// ...
}
- cookie만 삭제한다면 아래와 같이 작성하여 적용할 수 있다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
// ClearSiteDataHeader를 작성하는 LogoutHandler 생성 - cookie만 제거
HeaderWriterLogoutHandler clearSiteData =
new HeaderWriterLogoutHandler(
new ClearSiteDataHeaderWriter(Directive.COOKIES)
);
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// LogoutHanler 추가
.logout((logout) -> logout
.addLogoutHandler(clearSiteData)
);
// ...
}
Logout 성공 커스터마이징
LogoutSuccessHandler는 logout 성공 동작을 커스터마이징할 수 있는 Spring Security Component다.- 함수적 인터페이스이므로 lambda를 사용하여 커스터마이징할 수 있다.
- 아래 공식 문서 예시는 logout 성공 시 리다이렉트하는 대신 status code를 반환하는 예시다.
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
http
.authorizeHttpRequests((authorize) -> authorize
// ...
)
// status code를 반환하는 LogoutSuccessHandler 추가
.logout((logout) -> logout
.logoutSuccessHandler(
new HttpStatusReturningLogoutSuccessHandler()
)
);
// ...
}
